/**
 *File: BlockLinkedList.h
 *Author: Diane, Dan, and Tim
 *Created September 2012
 */

#ifndef _BLOCK_LINKED_LIST_H
#define _BLOCK_LINKED_LIST_H

#include "Disk.h"
#include "FSBlock.h"
#include "CannotReadException.h"

/**
 * This class represents a non-contiguous sequence of blocks on the disk.
 * This can be used to represent sequential files. This utilizes 32 bit
 * pointers ('int' values) to link together disk blocks. Thus, part of
 * the disk blocks are used for representing the allocation of blocks.
 * The pointers are stored as the first four bytes of each block
 * ("next pointers"). Block numbers used in this class are the same as block
 * numbers used in the Disk class.
 *
 * The Rewind method enables you to start accessing a BlockLinkedList at its
 * first block. The GetCurrentBlock method returns the current block in the
 * BlockLinkedList, and GetNextBlock advances the BlockLinkedList to its next
 * block. Thus, GetCurrentBlock and GetNextBlock together act as a sequential
 * block access operation. You can change the contents of a block on Disk
 * using the Replace method.
 *
 * This class does not provide a way to write the BlockLinkedList data members
 * to disk. E.g., it does not provide a way to write the starting block number
 * or ending block number for a BlockLinkedList to disk. To create a new
 * BlockLinkedList, the caller decides on the block that forms the start of the
 * linked list, then calls the Initialize method of this class. Other blocks
 * can be added to that list if they are not yet part of a list, using AddBlock.
 * @author Timothy Millard
 * @author Chris Prince
 */

class BlockLinkedList {
    public:
        /** Default initialization for all data members. Does not modify the Disk.
         */
        BlockLinkedList();

        /** Uses the disk and block size given to initialize those data members of
         * the object. Other data members get default initialization.
         * Does not modify the Disk.
         *
         * @param disk to use in operations on the BlockLinkedList thus constructed.
         * @param blockSize	size of the blocks in this BlockLinkedList.
         */
        BlockLinkedList(Disk * disk, unsigned int blockSize);

        /** Create a new BlockLinkedList using the disk, and block size of another
         * BlockLinkedList.
         * Does not modify the Disk.
         *
         * @param Data member values are copied over from this other BlockLinkedList.
         */
        BlockLinkedList(BlockLinkedList * b);

        /** Add another block to the end of this BlockLinkedList. Makes changes
         * on disk. Assumes that the ending block number for this
         * BlockLinkedList is valid. Does *not* change the state of the m_seek
         * current block, but if the m_seek block is changed by this AddBlock,
         * on disk, this will refresh the m_seek block into RAM.
         *
         * @param newBlock is the new Block. This block should have already
         * been removed from the BlockLinkedList upon which it was previously.
         * Resets the data in the m_buffer of this block before
         * writing to disk.
         */
        bool AddBlock(FSBlock * newBlock) throw(CannotReadException);


        /** Count the number of blocks in a BlockLinkedList by traversing the
         * Blocks from the start Block to the end Block. This may be useful for
         * debugging. Changes the Rewind state of the BlockLinkedList.
         * @return int
         */
        int CountBlocks();

        /** Returns the amount of "usable" data within each block on disk.
         * @return Returns the block size in bytes of the blocks associated
         * with the BlockLinkedList. The returned value will be 4 bytes less
         * than the block size of the Disk associated with this BlockLinkedList
         * because the first four bytes of each block's m_buffer are reserved
         * for the "next pointer".
         */
        int GetBlockSize(){
		return m_blockSize;
	}

        /** Returns a reference to the current Block in the Rewind/ GetNextBlock sequence.
         */
        FSBlock* GetCurrentBlock();

        /**
         * @return the Disk object associated with this BlockLinkedList.
         */
        Disk* GetDisk(){
		return m_disk;
	}



        /** Return the ending block number for the BlockLinkedList.
         */
        int GetEndBlockNumber();



        /** Seeks the BlockLinkedList to the next block, unless
         * GetCurrentBlock() returns null. Changes the value returned by
         * GetCurrentBlock() to the next Block in the file, unless no more
         * Blocks exist. In this case, GetCurrentBlock() will now return null.
         * No changes are made to disk.
         */
        void GetNextBlock();



        /**
         * Return the number of blocks currently in this BlockLinkedList.
         */
        int GetNumberOfBlocks(){
		return m_numberOfBlocks;
	}

        /**
         * @return the starting block number for the BlockLinkedList.
         */
        int GetStartBlockNumber(){
		return startBlockNumber;
	}

        /** Generates a one block BlockLinkedList. Modifies the disk. Assumes
         * the disk and block size data members of this BlockLinkedList have
         * been properly initialized.
         * @return blockNumber is the block number on disk of the first
         * (and only) block of this BlockLinkedList. Resets the data in the
         * m_buffer of this block before writing to disk.
         */
        bool Initialize(int blockNumber);



        /** Outputs to the console the sequence of block numbers comprising
         * this BlockLinkedList. Rewind state is changed by this call.
         */
        void Output();

        /** Replace data
         *
         * The idea in this method is to replace the data of the m_seek Block
         * (i.e., the GetCurrentBlock() Block) but to leave the link structure
         * of the BlockLinkedList alone and unchanged. Replaces the last
         * blk.GetBlockSize()-4 bytes of the GetCurrentBlock() Block on Disk of
         * this BlockLinkedList with the last blk.GetBlockSize()-4 bytes of the
         * m_buffer of the block given as a parameter. The first four bytes of
         * the m_seek Block (i.e., the GetCurrentBlock() Block) are retained.
         * Modifies the Disk.
         * @return FIXME
         */
        bool Replace(FSBlock * blk);

        /**
         * After this call, GetCurrentBlock will return the first block in the
         * BlockLinkedList. After this call, a call to GetNextBlock will seek
         * to the second Block (if any) in the BlockLinkedList. No changes are
         * made to disk.
         */

        void Rewind();

        /**
         * Unlinks the block that is the starting block of this BlockLinkedList. Makes changes on disk.
         * @return FSBlock representing the unlinked block. This Block isn't
         * linked into any BlockLinkedList. The next pointer of the Block is
         * now 0. It is the responsibility of the caller to deallocate the
         * returned Block.
         */
        FSBlock * UnlinkBlock();

	static boid Test();


    protected:
	void Init(BlockLinkedList *b);

        Disk* m_disk;
	unsigned int m_blockSize;
	unsigned int m_numberOfBlocks;
	unsigned int m_startBlockNumber
	unsigned int m_endBlockNumber;
	unsigned int m_currentBlockNumber;
	FSBlock *m_seek;
	static const bool DEBUG = false;
	static const int DEFAULT_NUMBER_OF_BLOCKS = 100;
    };
#endif
